use godot::{global::Error, prelude::*};

pub struct Parser<'a> {
    source: &'a [char],
    p: usize,
    zero: Variant,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a [char]) -> Self {
        Self {
            source,
            p: 0,
            zero: Variant::from(0),
        }
    }

    fn current(&self) -> Option<char> {
        if self.p < self.source.len() {
            Some(self.source[self.p])
        } else {
            None
        }
    }

    fn move_next(&mut self) {
        self.p += 1;
    }

    pub fn parse(&mut self) -> Result<Variant, Error> {
        while let Some(ch) = self.current() {
            if ch.is_whitespace() {
                self.move_next();
            } else {
                break;
            }
        }

        if let Some(ch) = self.current() {
            match ch {
                '(' => self.parse_array(),
                '#' => self.parse_boolean(),
                '.' => self.parse_float(0).map(Variant::from),
                '0'..='9' => self.parse_number(),
                '"' => self.parse_string(),
                _ => self.parse_ident(),
            }
        } else {
            Ok(Variant::nil())
        }
    }

    pub fn parse_number(&mut self) -> Result<Variant, Error> {
        match self.current() {
            Some('0') => {
                self.move_next();
                let ch = self.current().map(|ch| ch.to_ascii_lowercase());
                match ch {
                    Some('x') => self.parse_number_radix(16),
                    Some('o') => self.parse_number_radix(8),
                    Some('b') => self.parse_number_radix(2),
                    Some('.') => self.parse_float(0).map(Variant::from),
                    Some('0'..='9') => self.parse_number(),
                    Some(')' | ' ') => Ok(Variant::from(0)),
                    _ => Err(Error::ERR_PARSE_ERROR),
                }
            }
            Some(_) => {
                let mut num: i64 = 0;
                while let Some(digit) = self.current().map(|ch| ch.to_digit(10)).flatten() {
                    num *= 10;
                    num += digit as i64;
                    self.move_next();
                }

                match self.current() {
                    Some(')' | ' ') | None => Ok(Variant::from(num)),
                    Some('.') => self.parse_float(num).map(Variant::from),
                    _ => Err(Error::ERR_PARSE_ERROR),
                }
            }
            _ => Err(Error::ERR_PARSE_ERROR),
        }
    }

    fn parse_float(&mut self, int: i64) -> Result<f64, Error> {
        if let Some('.') = self.current() {
            self.move_next();
        } else {
            return Err(Error::ERR_PARSE_ERROR);
        }

        let mut ans = int as f64;
        let mut frac: f64 = 1.0;
        while let Some(digit) = self.current().map(|ch| ch.to_digit(10)).flatten() {
            frac /= 10.0;
            ans += digit as f64 * frac;
            self.move_next();
        }

        match self.current() {
            Some(' ' | ')') | None => Ok(ans),
            _ => Err(Error::ERR_PARSE_ERROR),
        }
    }

    fn parse_number_radix(&mut self, radix: u32) -> Result<Variant, Error> {
        if let Some(_) = self.current() {
            self.move_next();
        } else {
            return Err(Error::ERR_PARSE_ERROR);
        }

        let mut num = 0i64;

        while let Some(digit) = self.current().map(|ch| ch.to_digit(radix)).flatten() {
            num *= radix as i64;
            num += digit as i64;
            self.move_next();
        }

        if let Some(')' | ' ') | None = self.current() {
            Ok(Variant::from(num))
        } else {
            Err(Error::ERR_PARSE_ERROR)
        }
    }

    pub fn parse_boolean(&mut self) -> Result<Variant, Error> {
        if let Some('#') = self.current() {
            self.move_next();
        } else {
            return Err(Error::ERR_PARSE_ERROR);
        }

        let ans = match self.current() {
            Some('t') => true,
            Some('f') => false,
            _ => return Err(Error::ERR_PARSE_ERROR),
        };

        self.move_next();

        Ok(Variant::from(ans))
    }

    fn parse_array(&mut self) -> Result<Variant, Error> {
        if let Some('(') = self.current() {
            self.move_next();
        } else {
            return Err(Error::ERR_PARSE_ERROR);
        }

        let mut compound = VariantArray::new();

        loop {
            match self.current() {
                Some(')') => {
                    self.move_next();
                    break;
                }
                Some(' ') => {
                    self.move_next();
                }
                Some(_) => compound.push(self.parse()?),
                _ => return Err(Error::ERR_PARSE_ERROR),
            }
        }

        if compound.len() > 0 {
            Ok(compound.to_variant())
        } else {
            Ok(Variant::nil())
        }
    }

    fn parse_string(&mut self) -> Result<Variant, Error> {
        if let Some('"') = self.current() {
            self.move_next();
        } else {
            return Err(Error::ERR_PARSE_ERROR);
        }

        let mut string = String::new();

        loop {
            match self.current() {
                Some('"') => {
                    self.move_next();
                    break;
                }
                Some('\\') => {
                    self.move_next();
                    match self.current() {
                        Some('r') => string.push('\r'),
                        Some('t') => string.push('\t'),
                        Some('n') => string.push('\n'),
                        Some('\\') => string.push('\\'),
                        _ => return Err(Error::ERR_PARSE_ERROR),
                    }
                }
                Some(ch) => string.push(ch),
                _ => return Err(Error::ERR_PARSE_ERROR),
            }
            self.move_next();
        }

        Ok(string.to_variant())
    }

    fn parse_ident(&mut self) -> Result<Variant, Error> {
        let mut name = String::new();

        if let Some(ch) = self.current() {
            match ch {
                ')' | ' ' => return Err(Error::ERR_PARSE_ERROR),
                _ => name.push(ch),
            }
            self.move_next();
        }

        if let Some(ch) = self.current() {
            match ch {
                ')' | ' ' => return Ok(StringName::from(name).to_variant()),
                _ if ch.is_ascii_digit() => {
                    return self.parse_number().map(|v| {
                        self.zero
                            .evaluate(&v, VariantOperator::SUBTRACT)
                            .unwrap_or_default()
                    })
                }
                _ => name.push(ch),
            }
            self.move_next();
        }

        while let Some(ch) = self.current() {
            match ch {
                ')' | ' ' => break,
                _ => name.push(ch),
            }
            self.move_next();
        }

        Ok(StringName::from(name).to_variant())
    }
}
